﻿using System;
using System.Collections.Specialized;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Xpedite.Extensions;
using Xpedite.Handlers;
using Xpedite.Resources;
using Xpedite.Resources.Builders;
using Xpedite.Resources.Combiners;
using Xpedite.Resources.Compressors;
using Xpedite.Resources.Definitions;
using Xpedite.WebAbstraction;

/* Copyright (c) 2010 CBaxter
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
 * IN THE SOFTWARE. 
 */

namespace Xpedite.Test.Handlers
{
  [TestClass]
  public class VirtualResourceHandlerTest
  {
    private readonly Mock<ICompressorFactory> _compressorFactory = new Mock<ICompressorFactory>(MockBehavior.Strict);
    private readonly Mock<IResourceContainer> _resourceContainer = new Mock<IResourceContainer>(MockBehavior.Strict);
    private readonly Mock<IResourceCache> _cache = new Mock<IResourceCache>(MockBehavior.Strict);
    private readonly Mock<ICompressor> _compressor = new Mock<ICompressor>(MockBehavior.Strict);
    private readonly Mock<IWebResponse> _response = new Mock<IWebResponse>(MockBehavior.Strict);
    private readonly Mock<IWebRequest> _request = new Mock<IWebRequest>(MockBehavior.Strict);
    private readonly Mock<IWebContext> _context = new Mock<IWebContext>(MockBehavior.Strict);
    private readonly Mock<IResource> _resource = new Mock<IResource>(MockBehavior.Strict);
    private readonly VirtualResourceHandler _handler;

    public VirtualResourceHandlerTest()
    {
      var definitionArgs = new DefinitionArgs(
          _cache.Object,
          new Mock<IResourceUrlGenerator>(MockBehavior.Strict).Object,
          new Mock<IResourceBuilderFactory>(MockBehavior.Strict).Object,
          new Mock<ICombinerFactory>(MockBehavior.Strict).Object
        );
      
      _context.SetupGet(mock => mock[WebContextExtensions.DefinitionArgsKey]).Returns(definitionArgs);
      _context.SetupGet(mock => mock.Response).Returns(_response.Object);
      _context.SetupGet(mock => mock.Request).Returns(_request.Object);

      _request.SetupGet(mock => mock.ApplicationPath).Returns("/");
      _request.Setup(mock => mock.MapPath("/resource.url")).Returns(String.Empty);
      _request.SetupGet(mock => mock.Url).Returns(new Uri("http://localhost/resource.url"));

      _handler = new VirtualResourceHandler(_compressorFactory.Object);
    }

    [TestMethod]
    public void TestProcessRequestWhereResourceNotFound()
    {
      _cache.Setup(mock => mock.Get(It.Is<IResourceUrl>(value => value.VirtualPath == "/resource.url"))).Returns((IResourceContainer)null);
      _response.Setup(mock => mock.NotFound());

      _handler.ProcessRequest(_context.Object);
    }

    [TestMethod]
    public void TestProcessRequestWhereResourceFoundAndNotModified()
    {
      var lastModified = DateTime.Now;

      _cache.Setup(mock => mock.Get(It.Is<IResourceUrl>(value => value.VirtualPath == "/resource.url"))).Returns(_resourceContainer.Object);
      _resourceContainer.SetupGet(mock => mock.Resource).Returns(_resource.Object);
      _resource.SetupGet(mock => mock.LastModified).Returns(lastModified);
      _request.SetupGet(mock => mock.Headers).Returns(new NameValueCollection { { WebRequestHeader.IfModifiedSince, lastModified.ToString(DateFormat.RFC1123) } });
      _response.Setup(mock => mock.NotModified());

      _handler.ProcessRequest(_context.Object);
    }

    [TestMethod]
    public void TestProcessRequestWhereResourceFoundAndModified()
    {
      var lastModified = DateTime.Now;

      _cache.Setup(mock => mock.Get(It.Is<IResourceUrl>(value => value.VirtualPath == "/resource.url"))).Returns(_resourceContainer.Object);
      _resourceContainer.SetupGet(mock => mock.Resource).Returns(_resource.Object);
      _resource.SetupGet(mock => mock.LastModified).Returns(lastModified);
      _request.SetupGet(mock => mock.Headers).Returns(new NameValueCollection { { WebRequestHeader.IfModifiedSince, lastModified.Date.ToString(DateFormat.RFC1123) }, { WebRequestHeader.AcceptEncoding, "gzip, deflate" } });
      _compressorFactory.Setup(mock => mock.Create("gzip, deflate")).Returns(_compressor.Object);
      _response.Setup(mock => mock.SendResource(_resource.Object, _compressor.Object));

      _handler.ProcessRequest(_context.Object);
    }

    [TestMethod]
    public void TestProcessRequestWhereResourceFoundWithException()
    {
      Exception ex = new Exception();

      _cache.Setup(mock => mock.Get(It.Is<IResourceUrl>(value => value.VirtualPath == "/resource.url"))).Returns(_resourceContainer.Object);
      _resourceContainer.SetupGet(mock => mock.Resource).Throws(ex);
      _cache.Setup(mock => mock.Remove(It.Is<IResourceUrl>(value => value.VirtualPath == "/resource.url")));

      Expect.Exception(() => _handler.ProcessRequest(_context.Object), ex);
    }
  }
}
